using Microsoft.AspNetCore.SignalR;
using TerritoryGame.Application.Services;
using TerritoryGame.Application.Interfaces;
using TerritoryGame.Domain.Entities.App;
using System.Collections.Concurrent;

namespace TerritoryGame.Api.Hubs
{
    public class GameHub : Hub
    {
        private readonly IAppGameService _gameService;
        private readonly ILogger<GameHub> _logger;
        private readonly IGameCacheService _cache;
        private readonly IAppGameHistoryService _gameHistory;
        private readonly IAppGameStatsService _gameStats;
        private readonly IAppUserService _userService;
        private readonly IAppUserLevelService _userLevelService;

        // 连接ID到(roomId, playerId)的快速映射（仅Hub进程内存）
        private static readonly ConcurrentDictionary<string, (string roomId, string playerId)> _connectionMap = new();

        public GameHub(
            IAppGameService gameService,
            ILogger<GameHub> logger,
            IGameCacheService cache,
            IAppGameHistoryService gameHistory,
            IAppGameStatsService gameStats,
            IAppUserService userService,
            IAppUserLevelService userLevelService)
        {
            _gameService = gameService;
            _logger = logger;
            _cache = cache;
            _gameHistory = gameHistory;
            _gameStats = gameStats;
            _userService = userService;
            _userLevelService = userLevelService;
        }
        private async Task<string> ResolveUsernameAsync(string userId, string fallback)
        {
            try
            {
                var res = await _userService.GetUserByIdAsync(Guid.Parse(userId));
                if (res?.Data is TerritoryGame.Application.Dtos.UserListDto dto)
                {
                    if (!string.IsNullOrWhiteSpace(dto.UserName)) return dto.UserName;
                }
            }
            catch { }
            return fallback;
        }

        private async Task<string?> ResolveAvatarAsync(string userId)
        {
            try
            {
                var res = await _userService.GetUserByIdAsync(Guid.Parse(userId));
                if (res?.Data is TerritoryGame.Application.Dtos.UserListDto dto)
                {
                    return dto.Avatar;
                }
            }
            catch { }
            return null;
        }

        public override async Task OnConnectedAsync()
        {
            _logger.LogInformation("Client connected: {ConnectionId}", Context.ConnectionId);
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception? exception)
        {
            var connectionId = Context.ConnectionId;
            _logger.LogInformation("Client disconnected: {ConnectionId}", connectionId);

            if (_connectionMap.TryRemove(connectionId, out var info))
            {
                await HandlePlayerDisconnect(info.roomId, info.playerId);
            }

            await base.OnDisconnectedAsync(exception);
        }

        // 玩家准备/取消准备（Redis缓存 + 广播）
        public async Task PlayerReady(PlayerReadyRequest request)
        {
            try
            {
                if (string.IsNullOrEmpty(request.RoomId))
                {
                    await Clients.Caller.SendAsync("PlayerReadyFailed", new { message = "无效的请求" });
                    return;
                }

                var room = await _cache.GetGameRoomAsync(request.RoomId);
                if (room == null)
                {
                    await Clients.Caller.SendAsync("PlayerReadyFailed", new { message = "房间不存在" });
                    return;
                }

                // 1) 按照请求中的 PlayerId 查找
                var player = !string.IsNullOrWhiteSpace(request.PlayerId)
                    ? room.Players.FirstOrDefault(p => p.Id.ToString() == request.PlayerId)
                    : null;

                // 2) 兜底：从连接的用户身份解析真实用户ID再匹配（避免客户端传错/缺失 playerId）
                if (player == null)
                {
                    try
                    {
                        var userIdClaim = Context.User?.Claims?.FirstOrDefault(c =>
                            c.Type == "sub" || c.Type == "user_id" || c.Type == "uid" ||
                            c.Type.EndsWith("nameidentifier", StringComparison.OrdinalIgnoreCase))?.Value;
                        if (!string.IsNullOrWhiteSpace(userIdClaim))
                        {
                            player = room.Players.FirstOrDefault(p => p.Id.ToString() == userIdClaim);
                        }
                    }
                    catch { }
                }

                // 3) 再兜底：根据连接映射找到该连接对应的玩家ID
                if (player == null)
                {
                    try
                    {
                        if (_connectionMap.TryGetValue(Context.ConnectionId, out var info))
                        {
                            var mappedPlayerId = info.playerId;
                            if (!string.IsNullOrWhiteSpace(mappedPlayerId))
                            {
                                player = room.Players.FirstOrDefault(p => p.Id.ToString() == mappedPlayerId);
                            }
                        }
                    }
                    catch { }
                }

                if (player == null)
                {
                    await Clients.Caller.SendAsync("PlayerReadyFailed", new { message = "玩家不存在或未加入房间" });
                    return;
                }

                player.IsReady = request.IsReady;
                await _cache.CachePlayerAsync(room.Id.ToString(), player);
                await _cache.CacheGameRoomAsync(room);

                // 通知房间内所有人（新事件名）
                await Clients.Group(room.Id.ToString()).SendAsync("MemberReadyChanged", new
                {
                    roomId = room.Id.ToString(),
                    playerId = player.Id.ToString(),
                    username = player.Username,
                    isReady = player.IsReady
                });

                // 兼容旧前端：同时广播旧事件名
                await Clients.Group(room.Id.ToString()).SendAsync("PlayerReadyChanged", new
                {
                    roomId = room.Id.ToString(),
                    playerId = player.Id.ToString(),
                    username = player.Username,
                    isReady = player.IsReady
                });

                // 回执给自己（避免客户端漏收群发时不同步）
                await Clients.Caller.SendAsync("MemberReadyChanged", new
                {
                    roomId = room.Id.ToString(),
                    playerId = player.Id.ToString(),
                    username = player.Username,
                    isReady = player.IsReady
                });

                // 兼容旧前端：向自己回执旧事件名
                await Clients.Caller.SendAsync("PlayerReadyChanged", new
                {
                    roomId = room.Id.ToString(),
                    playerId = player.Id.ToString(),
                    username = player.Username,
                    isReady = player.IsReady
                });

                _logger.LogInformation("Player {Player} ready={Ready} in room {Room}", player.Username, player.IsReady, room.Id);

                // 额外：向调用者单独回执一次（已经保留），避免某些客户端偶发漏收群发包
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to set ready state for player {PlayerId}", request.PlayerId);
                await Clients.Caller.SendAsync("PlayerReadyFailed", new { message = "设置准备状态失败" });
            }
        }

        // 创建房间（Redis缓存）
        public async Task CreateRoom(CreateRoomRequest request)
        {
            try
            {
                _logger.LogInformation("Creating room: {RoomName}", request.RoomName);

                // 验证请求
                if (string.IsNullOrEmpty(request.RoomName) || request.MaxPlayers < 2 || request.MaxPlayers > 6)
                {
                    await Clients.Caller.SendAsync("RoomCreationFailed", new { message = "无效的房间参数" });
                    return;
                }

                // 绑定创建者为当前登录用户，并解析真实用户名
                try
                {
                    var userId = Context.User?.Claims?.FirstOrDefault(c =>
                        c.Type == "sub" || c.Type == "user_id" || c.Type == "uid" ||
                        c.Type.EndsWith("nameidentifier", StringComparison.OrdinalIgnoreCase))?.Value;
                    if (!string.IsNullOrWhiteSpace(userId))
                    {
                        if (Guid.TryParse(userId, out var guid)) request.Creator.Id = guid;
                        var tokenName = Context.User?.Identity?.Name
                            ?? (Context.User?.Claims?.FirstOrDefault(c => c.Type.EndsWith("name", StringComparison.OrdinalIgnoreCase))?.Value)
                            ?? ($"User_{userId[..Math.Min(6, userId.Length)]}");
                        request.Creator.Username = await ResolveUsernameAsync(userId, tokenName);
                    }
                }
                catch { }

                // 创建房间
                var room = new GameRoom
                {
                    RoomName = request.RoomName,
                    MaxPlayers = request.MaxPlayers,
                    GameDuration = request.GameDuration,
                    Status = GameStatus.Waiting,
                    Creator = request.Creator,
                    Players = new List<Player> { request.Creator },
                    CreatedAt = DateTime.UtcNow
                };

                // 为创建者分配颜色
                request.Creator.Color = GeneratePlayerColor(room.Players);
                request.Creator.ConnectionId = Context.ConnectionId;

                // 缓存房间与玩家
                await _cache.CacheGameRoomAsync(room);
                await _cache.CachePlayerAsync(room.Id.ToString(), request.Creator);
                await _cache.SetRoomStatusAsync(room.Id.ToString(), room.Status.ToString());
                await _cache.AddOnlineRoomAsync(room.Id.ToString());

                // 将创建者加入房间组
                await Groups.AddToGroupAsync(Context.ConnectionId, room.Id.ToString());
                _connectionMap[Context.ConnectionId] = (room.Id.ToString(), request.Creator.Id.ToString());

                // 发送成功响应
                await Clients.Caller.SendAsync("RoomCreated", new
                {
                    room = new
                    {
                        id = room.Id,
                        roomName = room.RoomName,
                        maxPlayers = room.MaxPlayers,
                        gameDuration = room.GameDuration,
                        status = room.Status.ToString(),
                        creator = room.Creator,
                        currentPlayers = room.Players.Count
                    }
                });

                _logger.LogInformation("Room created successfully: {RoomId}", room.Id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to create room: {RoomName}", request.RoomName);
                await Clients.Caller.SendAsync("RoomCreationFailed", new { message = "创建房间失败" });
            }
        }

        // 加入房间（Redis缓存）
        public async Task JoinRoom(JoinRoomRequest request)
        {
            try
            {
                _logger.LogInformation("Player {PlayerName} joining room: {RoomId}", request.Player.Username, request.RoomId);

                // 查找房间
                var room = await _cache.GetGameRoomAsync(request.RoomId);
                if (room == null)
                {
                    await Clients.Caller.SendAsync("RoomJoinFailed", new { message = "房间不存在" });
                    return;
                }

                // 从 Token 解析真实用户ID，覆盖客户端传来的 player.Id，确保与用户系统一致
                Guid claimedGuid = Guid.Empty;
                try
                {
                    var userIdClaim = Context.User?.Claims?.FirstOrDefault(c =>
                        c.Type == "sub" || c.Type == "user_id" || c.Type == "uid" ||
                        c.Type.EndsWith("nameidentifier", StringComparison.OrdinalIgnoreCase))?.Value;
                    if (!string.IsNullOrWhiteSpace(userIdClaim)) Guid.TryParse(userIdClaim, out claimedGuid);
                }
                catch { }
                if (claimedGuid != Guid.Empty) request.Player.Id = claimedGuid;

                // 检查玩家是否已在房间中（幂等）：已在房间则无视状态/容量限制，直接确保加入组
                var existing = room.Players.FirstOrDefault(p => p.Id == request.Player.Id);
                if (existing != null)
                {
                    // 已在房间：只确保加入SignalR组并更新映射，然后给调用方返回成功
                    await Groups.AddToGroupAsync(Context.ConnectionId, room.Id.ToString());
                    _connectionMap[Context.ConnectionId] = (room.Id.ToString(), existing.Id.ToString());

                    // 确保现存玩家的显示名来自用户系统
                    try
                    {
                        var resolved = await ResolveUsernameAsync(existing.Id.ToString(), existing.Username ?? $"User_{existing.Id.ToString().Substring(0,6)}");
                        if (!string.IsNullOrWhiteSpace(resolved) && existing.Username != resolved)
                        {
                            existing.Username = resolved;
                            await _cache.CachePlayerAsync(room.Id.ToString(), existing);
                            await _cache.CacheGameRoomAsync(room);
                        }
                    }
                    catch { }

                    await Clients.Caller.SendAsync("RoomJoined", new
                    {
                        room = new
                        {
                            id = room.Id,
                            roomName = room.RoomName,
                            maxPlayers = room.MaxPlayers,
                            gameDuration = room.GameDuration,
                            status = room.Status.ToString(),
                            creator = room.Creator,
                            currentPlayers = room.Players.Count
                        },
                        player = new {
                            id = existing.Id,
                            username = existing.Username,
                            avatar = await ResolveAvatarAsync(existing.Id.ToString()),
                            color = existing.Color,
                            areaCount = existing.AreaCount,
                            isReady = existing.IsReady
                        }
                    });

                    // 广播给房间组，确保其他客户端也能同步此连接加入（幂等安全）
                    await Clients.Group(room.Id.ToString()).SendAsync("PlayerJoined", new
                    {
                        roomId = room.Id.ToString(),
                        player = new {
                            id = existing.Id,
                            username = existing.Username,
                            avatar = await ResolveAvatarAsync(existing.Id.ToString()),
                            color = existing.Color,
                            areaCount = existing.AreaCount,
                            isReady = existing.IsReady
                        }
                    });

                    // 向所有客户端广播房间人数变化（用于大厅卡片实时更新）
                    await Clients.All.SendAsync("RoomPlayerCountChanged", new
                    {
                        roomId = room.Id.ToString(),
                        playerCount = room.Players.Count
                    });
                    return;
                }

                // 尚未在房间：此时再进行状态与容量检查
                // 允许在 Ready/Playing 阶段加入，以便中途进入的玩家也能同步和观战/参与
                // 仅当游戏已结束时禁止加入
                if (room.Status == GameStatus.Finished)
                {
                    await Clients.Caller.SendAsync("RoomJoinFailed", new { message = "游戏已结束，无法加入" });
                    return;
                }

                if (room.Players.Count >= room.MaxPlayers)
                {
                    await Clients.Caller.SendAsync("RoomJoinFailed", new { message = "房间已满" });
                    return;
                }

                // 为玩家设置显示名与颜色
                // 解析真实用户名（以用户服务为准，失败时回退 token/匿名）
                try
                {
                    var tokenName = Context.User?.Identity?.Name
                        ?? (Context.User?.Claims?.FirstOrDefault(c => c.Type.EndsWith("name", StringComparison.OrdinalIgnoreCase))?.Value)
                        ?? ($"User_{request.Player.Id.ToString().Substring(0, 6)}");
                    request.Player.Username = await ResolveUsernameAsync(request.Player.Id.ToString(), tokenName);
                }
                catch { }
                request.Player.Color = GeneratePlayerColor(room.Players);
                request.Player.ConnectionId = Context.ConnectionId;

                // 添加玩家到房间（用户名优先来自用户系统）
                try
                {
                    var tokenName = Context.User?.Identity?.Name
                        ?? (Context.User?.Claims?.FirstOrDefault(c => c.Type.EndsWith("name", StringComparison.OrdinalIgnoreCase))?.Value)
                        ?? ($"User_{request.Player.Id.ToString().Substring(0, 6)}");
                    request.Player.Username = await ResolveUsernameAsync(request.Player.Id.ToString(), tokenName);
                }
                catch { }
                room.Players.Add(request.Player);
                await _cache.CachePlayerAsync(room.Id.ToString(), request.Player);
                await _cache.CacheGameRoomAsync(room);

                // 将玩家加入房间组
                await Groups.AddToGroupAsync(Context.ConnectionId, room.Id.ToString());
                _connectionMap[Context.ConnectionId] = (room.Id.ToString(), request.Player.Id.ToString());

                // 通知房间内所有玩家
                await Clients.Group(room.Id.ToString()).SendAsync("PlayerJoined", new
                {
                    roomId = room.Id.ToString(),
                    player = new {
                        id = request.Player.Id,
                        username = request.Player.Username,
                        avatar = await ResolveAvatarAsync(request.Player.Id.ToString()),
                        color = request.Player.Color,
                        areaCount = request.Player.AreaCount
                    }
                });

                        // 发送完整的房间成员信息给所有玩家（包含等级和称号）
        var roomMembers = room.Players.Select(async p => {
            var userLevel = await _userLevelService.GetUserLevelAsync(p.Id);
            return new {
                id = p.Id,
                name = p.Username,
                avatar = await ResolveAvatarAsync(p.Id.ToString()),
                color = p.Color,
                isReady = p.IsReady,
                isHost = p.Id == room.Creator.Id,
                level = userLevel?.CurrentLevel ?? 1,
                title = userLevel?.CurrentTitle ?? "墨痕初绽"
            };
        }).ToList();

                var resolvedMembers = new List<object>();
                foreach (var memberTask in roomMembers)
                {
                    resolvedMembers.Add(await memberTask);
                }

                await Clients.Group(room.Id.ToString()).SendAsync("RoomMembersUpdated", new
                {
                    roomId = room.Id.ToString(),
                    members = resolvedMembers
                });

                // 发送成功响应
                await Clients.Caller.SendAsync("RoomJoined", new
                {
                    room = new
                    {
                        id = room.Id,
                        roomName = room.RoomName,
                        maxPlayers = room.MaxPlayers,
                        gameDuration = room.GameDuration,
                        status = room.Status.ToString(),
                        creator = room.Creator,
                        currentPlayers = room.Players.Count
                    },
                    player = new {
                        id = request.Player.Id,
                        username = request.Player.Username,
                        avatar = await ResolveAvatarAsync(request.Player.Id.ToString()),
                        color = request.Player.Color,
                        areaCount = request.Player.AreaCount
                    }
                });

                _logger.LogInformation("Player {PlayerName} joined room: {RoomId}", request.Player.Username, room.Id);

                // 向所有客户端广播房间人数变化（用于大厅卡片实时更新）
                await Clients.All.SendAsync("RoomPlayerCountChanged", new
                {
                    roomId = room.Id.ToString(),
                    playerCount = room.Players.Count
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to join room: {RoomId}", request.RoomId);
                await Clients.Caller.SendAsync("RoomJoinFailed", new { message = "加入房间失败" });
            }
        }

        // 离开房间（Redis缓存）
        public async Task LeaveRoom(LeaveRoomRequest request)
        {
            try
            {
                var mapping = _connectionMap.TryGetValue(Context.ConnectionId, out var info)
                    ? info
                    : default;

                var roomId = mapping.roomId;
                if (string.IsNullOrEmpty(roomId))
                {
                    return;
                }

                var room = await _cache.GetGameRoomAsync(roomId);
                if (room == null)
                {
                    return;
                }

                var player = room.Players.FirstOrDefault(p => p.Id.ToString() == request.PlayerId);
                if (player == null)
                {
                    return;
                }

                // 从房间移除玩家
                room.Players.Remove(player);
                await _cache.RemovePlayerAsync(room.Id.ToString(), player.Id);
                await _cache.CacheGameRoomAsync(room);

                // 从房间组移除
                await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomId);

                // 通知其他玩家
                await Clients.Group(roomId).SendAsync("PlayerLeft", new
                {
                    roomId = roomId,
                    playerId = request.PlayerId
                });

                // 如果房间空了，删除房间
                if (room.Players.Count == 0)
                {
                    await _cache.RemoveGameRoomAsync(roomId);
                    await _cache.RemoveOnlineRoomAsync(roomId);
                }
                // 如果游戏进行中且玩家离开，结束游戏
                else if (room.Status == GameStatus.Playing)
                {
                    await EndGame(roomId);
                }
                else
                {
                                    // 发送更新后的房间成员信息给剩余玩家（包含等级和称号）
                var roomMembers = room.Players.Select(async p => {
                    var userLevel = await _userLevelService.GetUserLevelAsync(p.Id);
                    return new {
                        id = p.Id,
                        name = p.Username,
                        avatar = await ResolveAvatarAsync(p.Id.ToString()),
                        color = p.Color,
                        isReady = p.IsReady,
                        isHost = p.Id == room.Creator.Id,
                        level = userLevel?.CurrentLevel ?? 1,
                        title = userLevel?.CurrentTitle ?? "墨痕初绽"
                    };
                }).ToList();

                    var resolvedMembers = new List<object>();
                    foreach (var memberTask in roomMembers)
                    {
                        resolvedMembers.Add(await memberTask);
                    }

                    await Clients.Group(roomId).SendAsync("RoomMembersUpdated", new
                    {
                        roomId = roomId,
                        members = resolvedMembers
                    });
                }

                _logger.LogInformation("Player {PlayerName} left room: {RoomId}", player.Username, roomId);

                // 广播房间人数变化（用于大厅卡片实时更新）
                await Clients.All.SendAsync("RoomPlayerCountChanged", new
                {
                    roomId = roomId,
                    playerCount = room.Players.Count
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to leave room for player: {PlayerId}", request.PlayerId);
            }
        }

        // 进入游戏界面（所有玩家同步进入）
        public async Task EnterGame(EnterGameRequest request)
        {
            try
            {
                var room = await _cache.GetGameRoomAsync(request.RoomId);
                if (room == null)
                {
                    await Clients.Caller.SendAsync("EnterGameFailed", new { message = "房间不存在" });
                    return;
                }

                // 检查是否是房主
                string? callerPlayerId = null;
                if (_connectionMap.TryGetValue(Context.ConnectionId, out var mapped))
                {
                    if (!string.IsNullOrWhiteSpace(mapped.playerId) && mapped.roomId == room.Id.ToString())
                    {
                        callerPlayerId = mapped.playerId;
                    }
                }
                if (string.IsNullOrWhiteSpace(callerPlayerId))
                {
                    try
                    {
                        var claimId = Context.User?.Claims?.FirstOrDefault(c =>
                            c.Type == "sub" || c.Type == "user_id" || c.Type == "uid" ||
                            c.Type.EndsWith("nameidentifier", StringComparison.OrdinalIgnoreCase))?.Value;
                        if (!string.IsNullOrWhiteSpace(claimId)) callerPlayerId = claimId;
                    }
                    catch { }
                }
                if (string.IsNullOrWhiteSpace(callerPlayerId))
                {
                    var byConn = room.Players.FirstOrDefault(p => p.ConnectionId == Context.ConnectionId);
                    callerPlayerId = byConn?.Id.ToString();
                }

                if (callerPlayerId != room.Creator.Id.ToString())
                {
                    await Clients.Caller.SendAsync("EnterGameFailed", new { message = "只有房主可以进入游戏" });
                    return;
                }

                // 设置房间状态为准备进入游戏
                room.Status = GameStatus.Waiting;
                await _cache.CacheGameRoomAsync(room);
                await _cache.SetRoomStatusAsync(room.Id.ToString(), room.Status.ToString());

                // 通知所有玩家进入游戏界面
                await Clients.Group(room.Id.ToString()).SendAsync("EnterGame", new
                {
                    roomId = room.Id.ToString(),
                    message = "房主已进入游戏界面，所有玩家请同步进入"
                });

                _logger.LogInformation("Room {RoomId} entered game mode", request.RoomId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to enter game for room: {RoomId}", request.RoomId);
                await Clients.Caller.SendAsync("EnterGameFailed", new { message = "进入游戏失败" });
            }
        }

        // 开始游戏（Redis缓存）
        public async Task StartGame(StartGameRequest request)
        {
            try
            {
                var room = await _cache.GetGameRoomAsync(request.RoomId);
                if (room == null)
                {
                    await Clients.Caller.SendAsync("GameStartFailed", new { message = "房间不存在" });
                    return;
                }

                // 更稳健地解析发起者身份：优先连接映射，其次 JWT，最后按连接ID匹配
                string? callerPlayerId = null;
                if (_connectionMap.TryGetValue(Context.ConnectionId, out var mapped))
                {
                    if (!string.IsNullOrWhiteSpace(mapped.playerId) && mapped.roomId == room.Id.ToString())
                    {
                        callerPlayerId = mapped.playerId;
                    }
                }
                if (string.IsNullOrWhiteSpace(callerPlayerId))
                {
                    try
                    {
                        var claimId = Context.User?.Claims?.FirstOrDefault(c =>
                            c.Type == "sub" || c.Type == "user_id" || c.Type == "uid" ||
                            c.Type.EndsWith("nameidentifier", StringComparison.OrdinalIgnoreCase))?.Value;
                        if (!string.IsNullOrWhiteSpace(claimId)) callerPlayerId = claimId;
                    }
                    catch { }
                }
                if (string.IsNullOrWhiteSpace(callerPlayerId))
                {
                    var byConn = room.Players.FirstOrDefault(p => p.ConnectionId == Context.ConnectionId);
                    callerPlayerId = byConn?.Id.ToString();
                }

                if (callerPlayerId != room.Creator.Id.ToString())
                {
                    await Clients.Caller.SendAsync("GameStartFailed", new { message = "只有房间创建者可以开始游戏" });
                    return;
                }

                // 检查准备状态：要求所有玩家均已准备（与前端提示一致）
                var host = room.Players.FirstOrDefault(p => p.Id.ToString() == room.Creator.Id.ToString());
                if (host == null || !host.IsReady)
                {
                    await Clients.Caller.SendAsync("GameStartFailed", new { message = "房主需要先准备" });
                    return;
                }
                var allReady = room.Players.All(p => p.IsReady);
                if (!allReady)
                {
                    await Clients.Caller.SendAsync("GameStartFailed", new { message = "还有玩家未准备就绪" });
                    return;
                }

                // 确保调用者已加入SignalR组（容错）
                try
                {
                    await Groups.AddToGroupAsync(Context.ConnectionId, room.Id.ToString());
                    _connectionMap[Context.ConnectionId] = (room.Id.ToString(), callerPlayerId!);
                }
                catch { }

                // 先回执给调用方，提示已受理开始请求（便于前端立即反馈）
                await Clients.Caller.SendAsync("StartGameAccepted", new { roomId = room.Id.ToString(), countdown = 5 });

                // 发送5秒倒计时开始通知（容错：失败不阻塞开始流程）
                try
                {
                    await Clients.Group(room.Id.ToString()).SendAsync("GameCountdown", new
                    {
                        roomId = room.Id.ToString(),
                        message = "游戏即将开始，倒计时5秒",
                        countdown = 5
                    });
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Failed to broadcast GameCountdown for room {RoomId}", room.Id);
                }

                // 等待5秒倒计时（若中途异常也不中断）
                try { await Task.Delay(5000); } catch { }

                // 开始游戏
                room.Status = GameStatus.Playing;
                room.StartTime = DateTime.UtcNow;
                await _cache.CacheGameRoomAsync(room);
                await _cache.SetRoomStatusAsync(room.Id.ToString(), room.Status.ToString());

                // 重置所有玩家面积（逐个容错，避免单个失败导致整体开始失败）
                foreach (var player in room.Players)
                {
                    try
                    {
                        player.AreaCount = 0;
                        await _cache.CachePlayerAsync(room.Id.ToString(), player);
                        await _cache.UpdatePlayerScoreAsync(room.Id.ToString(), player.Id, 0);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogWarning(ex, "Failed to reset area/score for player {PlayerId} in room {RoomId}", player.Id, room.Id);
                    }
                }

                // 通知所有玩家游戏开始（带剩余秒数，供前端倒计时）
                var totalSeconds = room.GameDuration * 60;
                await Clients.Group(room.Id.ToString()).SendAsync("GameStarted", new
                {
                    roomId = room.Id.ToString(),
                    duration = totalSeconds,
                    startTime = room.StartTime,
                    timeLeft = totalSeconds,
                    gameStatus = "playing"
                });

                // 发送房间成员信息给所有玩家（包含等级和称号）- 加强容错
                try
                {
                    var roomMembers = room.Players.Select(async p => {
                        try
                        {
                            var userLevel = await _userLevelService.GetUserLevelAsync(p.Id);
                            var avatar = await ResolveAvatarAsync(p.Id.ToString());
                            return new {
                                id = p.Id,
                                name = p.Username,
                                avatar = avatar,
                                color = p.Color,
                                isReady = p.IsReady,
                                isHost = p.Id == room.Creator.Id,
                                level = userLevel?.CurrentLevel ?? 1,
                                title = userLevel?.CurrentTitle ?? "墨痕初绽"
                            };
                        }
                        catch (Exception ex)
                        {
                            _logger.LogWarning(ex, "Failed to resolve level/avatar for player {PlayerId} in room {RoomId}", p.Id, room.Id);
                            return new {
                                id = p.Id,
                                name = p.Username,
                                avatar = (string?)null,
                                color = p.Color,
                                isReady = p.IsReady,
                                isHost = p.Id == room.Creator.Id,
                                level = 1,
                                title = "墨痕初绽"
                            };
                        }
                    }).ToList();

                    var resolvedMembers = new List<object>();
                    foreach (var memberTask in roomMembers)
                    {
                        try { resolvedMembers.Add(await memberTask); } catch { }
                    }

                    await Clients.Group(room.Id.ToString()).SendAsync("RoomMembersUpdated", new
                    {
                        roomId = room.Id.ToString(),
                        members = resolvedMembers
                    });
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Failed to broadcast RoomMembersUpdated for room {RoomId}", room.Id);
                }

                // 启动游戏计时器
                _ = Task.Run(async () => await StartGameTimer(room.Id.ToString(), room.GameDuration));

                _logger.LogInformation("Game started in room: {RoomId}", room.Id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to start game in room: {RoomId}", request.RoomId);
                // 返回更具体的信息以便前端诊断（生产可改为统一提示）
                await Clients.Caller.SendAsync("GameStartFailed", new { message = "开始游戏失败", detail = ex.Message });
            }
        }

        // 涂色动作（Redis缓存 + 排行榜）
        public async Task PaintAction(PaintActionRequest request)
        {
            try
            {
                _logger.LogInformation("PaintAction: 收到请求 {RoomId}, {X}, {Y}, {Color}, {PlayerId}", 
                    request.RoomId, request.X, request.Y, request.Color, request.PlayerId);
                
                var roomId = request.RoomId;
                var room = await _cache.GetGameRoomAsync(roomId);
                if (room == null)
                {
                    _logger.LogWarning("PaintAction: 房间不存在 {RoomId}", roomId);
                    return;
                }

                // 结束后不再接受绘画动作
                if (room.Status == GameStatus.Finished)
                {
                    _logger.LogInformation("PaintAction 忽略：房间已结束 {RoomId}", roomId);
                    return;
                }

                // 快速解析玩家ID
                string? playerId = null;
                string? playerName = null;
                
                // 优先从请求中获取
                if (!string.IsNullOrWhiteSpace(request.PlayerId))
                {
                    playerId = request.PlayerId;
                }
                else if (_connectionMap.TryGetValue(Context.ConnectionId, out var mapped))
                {
                    if (mapped.roomId == roomId)
                    {
                        playerId = mapped.playerId;
                    }
                }
                
                // 如果还是没有，从JWT获取
                if (string.IsNullOrWhiteSpace(playerId))
                {
                    try
                    {
                        var claimId = Context.User?.Claims?.FirstOrDefault(c =>
                            c.Type == "sub" || c.Type == "user_id" || c.Type == "uid" ||
                            c.Type.EndsWith("nameidentifier", StringComparison.OrdinalIgnoreCase))?.Value;
                        if (!string.IsNullOrWhiteSpace(claimId)) playerId = claimId;
                    }
                    catch { }
                }

                // 获取玩家名称
                if (!string.IsNullOrWhiteSpace(playerId))
                {
                    var player = room.Players.FirstOrDefault(p => p.Id.ToString() == playerId);
                    if (player != null)
                    {
                        playerName = player.Username;
                    }
                }

                // 确保已加入组
                try
                {
                    await Groups.AddToGroupAsync(Context.ConnectionId, roomId);
                    if (!string.IsNullOrWhiteSpace(playerId))
                    {
                        _connectionMap[Context.ConnectionId] = (roomId, playerId);
                    }
                }
                catch { }

                // 立即广播给房间内所有玩家
                var broadcastData = new
                {
                    roomId = roomId,
                    x = request.X,
                    y = request.Y,
                    color = request.Color,
                    playerName = playerName,
                    playerId = playerId,
                    brushSize = request.BrushSize,
                    timestamp = DateTime.UtcNow
                };

                // 异步广播，不等待完成
                _ = Clients.Group(roomId).SendAsync("PaintAction", broadcastData);

                // 异步更新缓存，不阻塞响应
                _ = Task.Run(async () =>
                {
                    try
                    {
                        if (!string.IsNullOrWhiteSpace(playerId) && Guid.TryParse(playerId, out var playerGuid))
                        {
                            await _cache.UpdatePlayerScoreAsync(roomId, playerGuid, 1);
                        }
                    }
                    catch { }
                });
                
                _logger.LogInformation("PaintAction: 成功处理并广播 {RoomId}, {PlayerId}", roomId, playerId);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "PaintAction处理失败: {RoomId}", request.RoomId);
            }
        }

        // 重置画布（Redis缓存）
        public async Task ResetCanvas(ResetCanvasRequest request)
        {
            try
            {
                var room = await _cache.GetGameRoomAsync(request.RoomId);
                if (room == null)
                {
                    return;
                }

                // 检查是否是房间创建者
                var caller = room.Players.FirstOrDefault(p => p.ConnectionId == Context.ConnectionId);
                if (caller?.Id.ToString() != room.Creator.Id.ToString())
                {
                    return;
                }

                // 重置游戏状态
                room.Status = GameStatus.Waiting;
                room.StartTime = null;
                await _cache.CacheGameRoomAsync(room);
                await _cache.SetRoomStatusAsync(room.Id.ToString(), room.Status.ToString());

                // 重置所有玩家面积
                foreach (var player in room.Players)
                {
                    player.AreaCount = 0;
                    await _cache.CachePlayerAsync(room.Id.ToString(), player);
                    await _cache.UpdatePlayerScoreAsync(room.Id.ToString(), player.Id, 0);
                }

                // 通知所有玩家画布重置
                await Clients.Group(room.Id.ToString()).SendAsync("CanvasReset", new
                {
                    roomId = room.Id.ToString()
                });

                _logger.LogInformation("Canvas reset in room: {RoomId}", room.Id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to reset canvas in room: {RoomId}", request.RoomId);
            }
        }

        // 游戏结果（构建记录、更新统计、广播）
        public async Task GameResult(GameResultRequest request)
        {
            try
            {
                var room = await _cache.GetGameRoomAsync(request.RoomId);
                if (room == null)
                {
                    return;
                }

                // 计算最终结果
                var players = await _cache.GetRoomPlayersAsync(request.RoomId);
                var sortedPlayers = players.OrderByDescending(p => p.AreaCount).ToList();
                var winner = sortedPlayers.FirstOrDefault(p => p.AreaCount > 0);

                var totalArea = sortedPlayers.Sum(p => p.AreaCount);

                // 构建并保存对局记录（内存服务）
                var record = new GameRecord
                {
                    RoomId = room.Id,
                    RoomName = room.RoomName,
                    WinnerId = winner?.Id, // 现在WinnerId是可空的
                    WinnerUsername = winner?.Username ?? string.Empty,
                    PlayerCount = sortedPlayers.Count,
                    Duration = room.GameDuration * 60,
                    StartedAt = room.StartTime ?? DateTime.UtcNow,
                    EndedAt = DateTime.UtcNow,
                    Status = GameStatus.Finished,
                    PlayerResults = sortedPlayers.Select((p, idx) => new PlayerGameResult
                    {
                        PlayerId = p.Id, // PlayerId现在也是可空的
                        Username = p.Username,
                        Color = p.Color,
                        AreaCount = p.AreaCount,
                        Rank = idx + 1,
                        IsWinner = winner != null && p.Id == winner.Id
                    }).ToList()
                };

                await _gameHistory.SaveGameRecordAsync(record);
                foreach (var p in sortedPlayers)
                {
                    await _gameStats.UpdatePlayerStatsAsync(p.Id.ToString(), record);
                }
                await _gameStats.UpdateRoomStatsAsync(room.Id.ToString(), record);

                // 通知所有玩家游戏结束
                await Clients.Group(room.Id.ToString()).SendAsync("GameEnded", new
                {
                    roomId = room.Id,
                    winner = winner,
                    rankings = sortedPlayers,
                    totalArea = totalArea,
                    gameDuration = room.GameDuration * 60
                });

                // 更新房间状态
                room.Status = GameStatus.Finished;
                room.EndTime = DateTime.UtcNow;
                await _cache.CacheGameRoomAsync(room);
                await _cache.SetRoomStatusAsync(room.Id.ToString(), room.Status.ToString());
                await _cache.SetRoomExpiryAsync(room.Id.ToString(), TimeSpan.FromMinutes(30));

                _logger.LogInformation("Game ended in room: {RoomId}, Winner: {Winner}", room.Id, winner?.Username ?? "None");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to process game result for room: {RoomId}", request.RoomId);
            }
        }

        // 私有方法
        private async Task HandlePlayerDisconnect(string roomId, string playerId)
        {
            await LeaveRoom(new LeaveRoomRequest { PlayerId = playerId });
        }

        private string GeneratePlayerColor(List<Player> existingPlayers)
        {
            var colors = new[]
            {
                "#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7",
                "#DDA0DD", "#98D8C8", "#F7DC6F", "#BB8FCE", "#85C1E9"
            };

            var usedColors = existingPlayers.Select(p => p.Color).ToHashSet();
            var availableColors = colors.Where(c => !usedColors.Contains(c)).ToArray();

            if (availableColors.Length > 0)
            {
                return availableColors[new Random().Next(availableColors.Length)];
            }

            // 如果没有可用颜色，生成随机颜色
            return $"#{new Random().Next(0x1000000):X6}";
        }

        private async Task StartGameTimer(string roomId, int durationMinutes)
        {
            await Task.Delay(durationMinutes * 60 * 1000); // 等待游戏时间结束

            var room = await _cache.GetGameRoomAsync(roomId);
            if (room != null && room.Status == GameStatus.Playing)
            {
                await EndGame(roomId);
            }
        }

        private async Task EndGame(string roomId)
        {
            var room = await _cache.GetGameRoomAsync(roomId);
            if (room == null)
            {
                return;
            }

            // 获取最终排行榜
            var leaderboard = await _cache.GetRoomLeaderboardAsync(roomId);
            
            // 通知所有玩家游戏结束
            await Clients.Group(roomId).SendAsync("GameEnded", new
            {
                roomId = roomId,
                leaderboard = leaderboard,
                endTime = DateTime.UtcNow
            });

            // 更新房间状态
            room.Status = GameStatus.Finished;
            await _cache.CacheGameRoomAsync(room);
            await _cache.SetRoomStatusAsync(roomId, room.Status.ToString());

            // 记录游戏历史
            try
            {
                await _gameHistory.RecordGameResultAsync(roomId, leaderboard);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to record game result for room: {RoomId}", roomId);
            }
        }

        private async Task UpdateLeaderboard(string roomId)
        {
            try
            {
                var leaderboard = await _cache.GetRoomLeaderboardAsync(roomId);
                await Clients.Group(roomId).SendAsync("LeaderboardUpdated", new
                {
                    roomId = roomId,
                    leaderboard = leaderboard,
                    updateTime = DateTime.UtcNow
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to update leaderboard for room: {RoomId}", roomId);
            }
        }
    }

    // 请求模型
    public class CreateRoomRequest
    {
        public string RoomName { get; set; } = string.Empty;
        public int MaxPlayers { get; set; }
        public int GameDuration { get; set; }
        public Player Creator { get; set; } = null!;
    }

    public class JoinRoomRequest
    {
        public string RoomId { get; set; } = string.Empty;
        public Player Player { get; set; } = null!;
    }

    public class LeaveRoomRequest
    {
        public string PlayerId { get; set; } = string.Empty;
    }

    public class StartGameRequest
    {
        public string RoomId { get; set; } = string.Empty;
    }

    public class EnterGameRequest
    {
        public string RoomId { get; set; } = string.Empty;
    }

    public class PlayerReadyRequest
    {
        public string RoomId { get; set; } = string.Empty;
        public string PlayerId { get; set; } = string.Empty;
        public bool IsReady { get; set; }
    }

    public class PaintActionRequest
    {
        public string RoomId { get; set; } = string.Empty;
        public string PlayerId { get; set; } = string.Empty;
        public int X { get; set; }
        public int Y { get; set; }
        public string Color { get; set; } = string.Empty;
        public string Tool { get; set; } = string.Empty;
        public long Timestamp { get; set; }
        public int BrushSize { get; set; }
    }

    public class ResetCanvasRequest
    {
        public string RoomId { get; set; } = string.Empty;
    }

    public class GameResultRequest
    {
        public string RoomId { get; set; } = string.Empty;
        public Dictionary<string, int> Areas { get; set; } = new();
    }
}
